package cn.zhm;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * <p>
 *     NIO 服务端
 * </p>
 * @Author: ZhongHaoMing
 * @emali: zhonghaoming@bosssoft.com
 * @since: 1.8
 * @Date: 2021/10/26 - 14:46
 */
public class NioMain {

    /**
     * NIO服务
     */
    private static Selector selector;
    /**
     * 连接超时时间，ms
     */
    private final static long OUT_TIME=5000L;

    /**
     * 启动9999端口服务，开始监听
     * @param args
     */
    public static void main(String[] args){
        try {
            init(9999);
            listen();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }

    }

    /**
     * 监听是否有客户连接
     * @throws Exception
     */
    private static void listen() throws Exception{
        System.out.println("服务器启动： "+selector.isOpen());
        while (true){
            // 等待一个客户端连接
            selector.select(OUT_TIME);
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                iterator.remove();
                hand(key);
            }
        }
    }

    /**
     * 是否为新的客户端
     * @param key
     * @throws Exception
     */
    private static void hand(SelectionKey key) throws Exception{
        if(key.isAcceptable()){
            handAccept(key);
        }else if(key.isReadable()){
            handRead(key);
        }
    }

    /**
     * 新的客户端连接，分配资源给这个客户，准备读取用户输入的内容
     * @param key
     * @throws Exception
     */
    private static void handAccept(SelectionKey key) throws Exception{
        ServerSocketChannel socketChannel = (ServerSocketChannel) key.channel();
        SocketChannel channel = socketChannel.accept();
        channel.configureBlocking(false);
        channel.register(selector,SelectionKey.OP_READ);
    }

    /**
     * 简单的解析请求，并返回一个页面，并将连接关闭
     * @param key
     * @throws Exception
     */
    private static void handRead(SelectionKey key) throws Exception{
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer bf = ByteBuffer.allocate(1024);
        channel.read(bf);
        byte[] array = bf.array();
        String s = new String(array);
        Path path = printHttp(s);
        channel.write(ByteBuffer.wrap(ReturnHtml.ret(path.getPath()).getBytes(StandardCharsets.UTF_8)));
        channel.close();
    }

    /**
     * 处理相应头，并将基本内容返回，路径，类型，参数
     * @param str
     * @return
     */
    private static Path printHttp(String str){
        Path path = new Path();
        String[] requestHeader = str.split("\n");
        try {
            List<Param> list = null;
            String[] header = requestHeader[0].split(" ");
            String[] param = header[1].split("\\?");
            path.setHead(header[0]);
            path.setPath(param[0]);
            if("/".equals(path.getPath())){
                path.setPath("/index.html");
            }
            if(param.length>1){
                list = new LinkedList();
                String[] split = param[1].split("&");
                for (String s : split) {
                    Param param1 = new Param();
                    String[] split1 = s.split("=");
                    param1.setKey(split1[0]);
                    param1.setValue(split1[1]);
                    list.add(param1);
                }
            }
            path.setHttp(header[2]);
            path.setParams(list);
        }catch (Exception e){
            System.out.println("无法解析请求");
        }
        return path;
    }

    /**
     * 开启一个服务
     * @param port 端口
     * @throws Exception
     */
    private static void init(int port) throws Exception{
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        socketChannel.configureBlocking(false);
        socketChannel.socket().bind(new InetSocketAddress(port));
        selector = Selector.open();
        socketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

}
